﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ExpressionMapper
{
    using MappingExpressionGenerator = Func<Expression, Type, Expression, ParameterExpression, Expression>;

    /// <summary>
    /// Returns generator for List to List mappings.
    /// </summary>
    public static partial class Generators
    {
        public static MappingExpressionGenerator ListToList()
        {
            return (fromExpr, to, customMapping, instanceDictionary) => (IsEnumerable(fromExpr.Type) && IsList(to))
                                                        ? customMapping ??
                                                          GenerateListConversionExpression(fromExpr, to, instanceDictionary)
                                                        : null;
        }

        private static Expression GenerateListConversionExpression(Expression fromExpr, Type to, ParameterExpression instanceDictionary)
        {
            Debug.WriteLine(string.Format("\tListToList ({0}): {1} to {2}", fromExpr.NodeType,fromExpr.Type.Name,to.Name));

            Type fromElementType = GetCollectionElementType(fromExpr.Type);
            Type toElementType = GetCollectionElementType(to);

            MethodInfo convertAllMethod = typeof (Generators)
                .GetMethod("ConvertList", BindingFlags.Static | BindingFlags.Public)
                .MakeGenericMethod(fromElementType, toElementType);

            Expression convertExpr = Expression.Call(convertAllMethod, fromExpr, instanceDictionary);

            Expression ifNullExpr = Expression.Condition(
                 Expression.Equal(Expression.Constant(null, fromExpr.Type), fromExpr),
                        Expression.Constant(null, to),
                        convertExpr
                      );
            return ifNullExpr;
        }

        private static bool IsList(Type t)
        {
            return t.IsGenericType && t.GetGenericTypeDefinition() == typeof (List<>);
        }

        public static List<TOutput> ConvertList<TInput, TOutput>(IEnumerable<TInput> input,
                                                                ReferenceDictionary referenceDictionary)
        {
            if (input == null)
            {
                return null;
            }

            return input.Select(item => Mapper.Map<TInput, TOutput>(item, referenceDictionary)).ToList();
        }
    }
}